#include "inputmanager.h"
#include <iostream>
#include <utilities/utilities.h>

#ifdef WIN32
#include "directinputtype.h"
#else
#include "linuxinputtype.h"
#endif

InputManager& InputManager::inputManager(unsigned int inputsRequested)
{
  static InputManager gInputManager(inputsRequested);

  for (unsigned int i(gInputManager.numInputs()); i < inputsRequested; ++i)
  {
    InputType* tmp(gInputManager.newInput());
    if (tmp)
    {
      gInputManager.mInputMutex.lock();
      gInputManager.mInputs.push_back(tmp);
      gInputManager.mInputMutex.unlock();
    }
  }

  return gInputManager;
}

InputManager::InputManager(unsigned int inputsRequested, unsigned int pollFrequency)
  : mSleepTime(1000/pollFrequency)
{
  for(unsigned int i(0); i < inputsRequested; ++i)
  {
    InputType* tmp(newInput());
    if (tmp)
    {
      mInputs.push_back(tmp);
    }
  }
  start();
}

InputManager::~InputManager()
{
  join();
  InputVector::iterator it;
  for (it = mInputs.begin(); it != mInputs.end(); ++it)
  {
    delete *it;
    *it = 0;
  }
  mInputs.clear();
}

unsigned int InputManager::numInputs() const
{
  unsigned int ret(0);
  mInputMutex.lock();
  ret = mInputs.size();
  mInputMutex.unlock();
  return ret;
}

void InputManager::print() const
{
  InputVector::const_iterator it;
  mInputMutex.lock();
  for (it = mInputs.begin(); it != mInputs.end(); ++it)
  {
    std::cout << **it << std::endl;
  }
  mInputMutex.unlock();
}

void InputManager::poll()
{
  InputVector::iterator it;
  mInputMutex.lock();
  for (it = mInputs.begin(); it != mInputs.end(); ++it)
  {
    (*it)->poll();
    unsigned int input(it-mInputs.begin());

    InputData d((*it)->data());

    std::list<unsigned int> dlist(d.axisMovement());
    std::list<unsigned int>::const_iterator it2(dlist.begin());
    for (; it2 != dlist.end(); ++it2)
    {
      axisEvent(input, *it2, d.axis(*it2));
    }

    dlist = d.press();
    for (it2 = dlist.begin(); it2 != dlist.end(); ++it2)
    {
      buttonEvent(input, *it2, true);
    }

    dlist = d.release();
    for (it2 = dlist.begin(); it2 != dlist.end(); ++it2)
    {
      buttonEvent(input, *it2, false);
    }
  }
  mInputMutex.unlock();
}

void InputManager::axisEvent(unsigned int input, unsigned int axis, int value)
{
  mCallbackMutex.lock();
  CallbackList::const_iterator it(mCallbacks.begin());
  for (; it != mCallbacks.end(); ++it)
  {
    (*it)->axisEvent(input, axis, value);
  }
  mCallbackMutex.unlock();
}

void InputManager::buttonEvent(unsigned int input, unsigned int button, bool value)
{
  mCallbackMutex.lock();
  CallbackList::const_iterator it(mCallbacks.begin());
  for (; it != mCallbacks.end(); ++it)
  {
    (*it)->buttonEvent(input, button, value);
  }
  mCallbackMutex.unlock();
}

void InputManager::run()
{
  while (mRunning)
  {
    poll();
    Utilities::sleep(mSleepTime);
  }
}

std::string InputManager::name(unsigned int input) const
{
  std::string ret;

  if (input < numInputs())
  {
    mInputMutex.lock();
    ret = mInputs.at(input)->name();
    mInputMutex.unlock();
  }

  return ret;
}

InputData InputManager::data(unsigned int input) const
{
  InputData ret;

  if (input < numInputs())
  {
    mInputMutex.lock();
    ret = mInputs.at(input)->data();
    mInputMutex.unlock();
  }

  return ret;
}

void InputManager::registerCallback(InputCallback *callback)
{
  if (callback)
  {
    mCallbackMutex.lock();
    mCallbacks.push_back(callback);
    mCallbackMutex.unlock();
  }
}

void InputManager::deregisterCallback(InputCallback *callback)
{
  if (callback)
  {
    mCallbackMutex.lock();
    mCallbacks.remove(callback);
    mCallbackMutex.unlock();
  }
}

InputType* InputManager::newInput()
{
  InputType* ret(0);

#ifdef WIN32
  ret = new DirectInputType();
#else
  ret = new LinuxInputType();
#endif

  if (ret && !ret->isOpen())
  {
    std::cout << "InputManager: Failed to open input device" << std::endl;
    delete ret;
    ret = 0;
  }

  return ret;
}
